export const streamChunk = function* (chunk, chunkSize) {
    let len = chunk.byteLength;

    if (!chunkSize || len < chunkSize) {
        yield chunk;
        return;
    }

    let pos = 0;
    let end;

    while (pos < len) {
        end = pos + chunkSize;
        yield chunk.slice(pos, end);
        pos = end;
    }
}

export const readBytes = async function* (iterable, chunkSize, encode) {
    for await (const chunk of iterable) {
        yield* streamChunk(ArrayBuffer.isView(chunk) ? chunk : (await encode(String(chunk))), chunkSize);
    }
}

export const trackStream = (stream, chunkSize, onProgress, onFinish, encode) => {
    const iterator = readBytes(stream, chunkSize, encode);

    let bytes = 0;

    return new ReadableStream({
        type: 'bytes',

        async pull(controller) {
            const {done, value} = await iterator.next();

            if (done) {
                controller.close();
                onFinish();
                return;
            }

            let len = value.byteLength;
            onProgress && onProgress(bytes += len);
            controller.enqueue(new Uint8Array(value));
        },
        cancel(reason) {
            onFinish(reason);
            return iterator.return();
        }
    }, {
        highWaterMark: 2
    })
}
